using System;
using System.Net;
using System.Net.Sockets;
using NetDns;

namespace NetDns.Transport
{
	/// <summary>
	/// Summary description for UdpTransport.
	/// </summary>
	public class TcpTransport : AbstractTransport
	{

		public TcpTransport(IPEndPoint endpoint) : base(endpoint) {}
		public TcpTransport(IPAddress address) : base(address) {}
		public TcpTransport(IPAddress address, int port) : base(address, port) {}


		public override byte[] SendRequest(byte[] requestMessage)
		{
			// firstly, uniquely mark this request with an id
			unchecked
			{
				// substitute in an id unique to this lookup, the request has no idea about this
				requestMessage[0] = (byte)(uniqueId >> 8);
				requestMessage[1] = (byte) uniqueId;
			}

			// we'll be send and receiving TCP packets
			Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			
			// we will wait at most 2 second for a dns reply
			//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 2000);

			// send it off to the server
			socket.Connect(this.endpoint);
			socket.Send(requestMessage, requestMessage.Length, SocketFlags.None);

			byte[] buf = new byte[2];

			try
			{
				//just read the first to bytes (wait for response)
				int inL = socket.Receive(buf);
				if (inL != 2)
					throw new System.IO.IOException("DNS Server isn't responding");

				int messageLength = ReadShort(buf);
				byte[] responseMessage = new byte[messageLength];
				int receivedSoFar = 0;

				while(receivedSoFar < messageLength)
				{
					//Incremental read a few bytes with each interation until we have received the complete message
					receivedSoFar += socket.Receive(responseMessage, receivedSoFar, messageLength - receivedSoFar, SocketFlags.Partial);	
				}

				// make sure the message returned is ours
				if (responseMessage[0] == requestMessage[0] && responseMessage[1] == requestMessage[1])
				{
					// its a valid response - return it, this is our successful exit point
					return responseMessage;
				}

			}
			finally
			{
				socket.Close();
			}

			throw new NoResponseException();
		}

		private int ReadShort(byte[] buf)
		{
			return (short) (buf[0] << 8 | buf[1]);
		}

	}
}
